﻿namespace JumpJump.Runtime
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;

    /// <summary>
    /// this class use for define a absolute position
    /// 2 dimension X,Y
    /// </summary>
    [Serializable]
    public class Coordinate
    {
        #region Fields

        /// <summary>
        /// how many direction one coordinate have
        /// </summary>
        public const int DirectionCount = 6;

        /// <summary>
        /// x,y delta when go to different direction
        /// delta[i,0]: X
        /// delta[i,1]: Y
        /// new X = X + delta[directionID, 0]
        /// </summary>
        public static int[,] Delta = new int[6, 2]
        {
          { 0, 1 },
          { 1, 1 },
          { 1, 0 },
          { 0, -1 },
          { -1, -1 },
          { -1, 0 },
        };

        /// <summary>
        /// the margin of top for each Y
        /// coorhashY[i] means controls whose Y coordinate is i
        /// in UI have a property canvas.top = coorhashY[i]
        /// </summary>
        public static double[] CoorHashY = new double[18]
        {
            -1, 615, 580, 541, 502, 463, 424, 385.25, 346.5,
            307.75, 269, 230, 191, 152, 114, 75, 36, 0 
        };

        /// <summary>
        /// the margin of left of each X
        /// coorhashX[i] means controls whose X coordinate is i
        /// in UI have a property canvas.top = coorhashX[i]
        /// Careful: this X is not same as X in coordinate class
        /// X in coordinate is oblique (X1)
        /// X here is horizontal (X2)
        /// X2 = start(X1) + offset(X1, Y)
        /// </summary>
        public static double[] CoorHashX = new double[]
        {
            -1,
            0,
            21.333,
            44.666,
            67.333,
            90.832,
            114.207,
            136.707,
            160.082,
            183.914,
            206.832,
            228.832,
            254.332,
            276.666,
            298.332,
            323.332,
            344.832,
            369.582,
            391.332,
            414.748,
            437.998,
            460.123,
            485.123,
            508.623,
            531.498,
            550
        };

        /// <summary>
        /// each X in coordinate have a start value for X in horizontal
        /// start(X1) = EachXStartWithHashX[i]
        /// </summary>
        public static int[] EachXStartWithHashX = new int[]
        {
            -1, 1, 2, 3, 4, 1, 3, 5, 7, 9, 10, 11,
            12, 13, 19, 21, 23, 25 
        };

        /// <summary>
        /// initial X value
        /// </summary>
        public int X = -1;

        /// <summary>
        /// initial Y value
        /// </summary>
        public int Y = -1;

        /// <summary>
        /// name and value for each direction
        /// </summary>
        public enum Direction
        {
            //  0 1
            // 5 X 2
            //  4 3

            /// <summary>
            /// 0 position 
            /// </summary>
            UpLeft = 0,

            /// <summary>
            /// 1 position 
            /// </summary>
            UpRight = 1,

            /// <summary>
            /// 2 position 
            /// </summary>
            Right = 2,

            /// <summary>
            /// 3 position 
            /// </summary>
            DownRight = 3,

            /// <summary>
            /// 4 position 
            /// </summary>
            DownLeft = 4,

            /// <summary>
            /// 5 position 
            /// </summary>
            Left = 5,
        }

        #endregion

        #region Constructor

        /// <summary>
        /// Initializes a new instance of the Coordinate class.
        /// Constructor for input x,y
        /// </summary>
        /// <param name="x">x value</param>
        /// <param name="y">y value</param>
        public Coordinate(int x, int y)
        {
            this.X = x;
            this.Y = y;
        }

        /// <summary>
        /// Initializes a new instance of the Coordinate class.
        /// default constructor
        /// </summary>
        public Coordinate()
        {
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// for each (dx, dy), which stand for the mouse position
        /// calculate the (X,Y) value in chessboard
        /// </summary>
        /// <param name="dx">mouse position X</param>
        /// <param name="dy">mouse position Y</param>
        /// <param name="x">chessboard position X</param>
        /// <param name="y">chessboard position Y</param>
        public static void TransformDxToX(double dx, double dy, ref int x, ref int y)
        {
            int lineNumY = (int)((dy - 12.5) / 39);
            double pieceCenterY = lineNumY * 39 + 12.5;
            if (dy - pieceCenterY <= 12.5)
            {
                y = lineNumY;
                y = 17 - y;
            }
            else if (pieceCenterY + 39 - dy <= 12.5)
            {
                y = lineNumY + 1;
                y = 17 - y;
            }
            else
            {
                y = -1;
            }

            double k = Math.Sqrt(3.0);
            double b = 170;
            double disX = (k * (dx - 12.5) + 650 - dy - b) / 2;
            int lineNumX = (int)(disX / 40.1);
            double pieceCenterX = lineNumX * 40.1;
            if (Math.Abs(disX - pieceCenterX) <= 12.5)
            {
                x = lineNumX + 1;
            }
            else if (pieceCenterX + 39 - disX <= 12.5)
            {
                x = lineNumX + 2;
            }
            else
            {
                x = -1;
            }
        }

        /// <summary>
        /// for each (X, Y) in chessboard
        /// calculate the position of (dx, dy) in UI
        /// </summary>
        /// <param name="x">chessboard position X</param>
        /// <param name="y">chessboard position Y</param>
        /// <param name="dx">UI position X</param>
        /// <param name="dy">UI position Y</param>
        public static void TransformXToDx(int x, int y, ref double dx, ref double dy)
        {
            dy = CoorHashY[y];
            int tranX = EachXStartWithHashX[x];
            int offset = Validator.CoordinateRange[x, 1] - y;
            tranX = tranX + offset;
            dx = CoorHashX[tranX];
        }

        /// <summary>
        /// override tostring
        /// </summary>
        /// <returns>X = value, Y = value</returns>
        public override string ToString()
        {
            string str = string.Empty;
            str += "X = " + this.X + ", ";
            str += "Y = " + this.Y;
            return str;
        }

        /// <summary>
        /// override the hash code of this 
        /// </summary>
        /// <returns>(11,11) hash code is 1111</returns>
        public override int GetHashCode()
        {
            return this.X * 100 + this.Y;
        }

        /// <summary>
        /// override Equals
        /// if two coordinate have same X and Y, equals
        /// </summary>
        /// <param name="obj">object to compare</param>
        /// <returns>true or false</returns>
        public override bool Equals(object obj)
        {
            if (obj is Coordinate)
            {
                Coordinate send = obj as Coordinate;
                if (this.X == send.X && this.Y == send.Y)
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
            else
            {
                return false;
            }
        }

        /// <summary>
        /// Ovverride euqals, compare x,y direcitly
        /// </summary>
        /// <param name="x">x coordinate</param>
        /// <param name="y">coordinate</param>
        /// <returns>true or false</returns>
        public bool IsSameCoordinate(int x, int y)
        {
            if (this.X == x && this.Y == y)
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        /// <summary>
        /// for a given direction, calculate the coordinate
        /// </summary>
        /// <param name="dir">direction input</param>
        /// <returns>chessboard position</returns>
        public Coordinate GetNeighbour(Direction dir)
        {
            int x = -1;
            int y = -1;

            switch (dir)
            {
                case Direction.UpLeft:
                    x = this.X;
                    y = this.Y + 1;
                    break;
                case Direction.UpRight:
                    x = this.X + 1;
                    y = this.Y + 1;
                    break;
                case Direction.Right:
                    x = this.X + 1;
                    y = this.Y;
                    break;
                case Direction.DownRight:
                    x = this.X;
                    y = this.Y - 1;
                    break;
                case Direction.DownLeft:
                    x = this.X - 1;
                    y = this.Y - 1;
                    break;
                case Direction.Left:
                    x = this.X - 1;
                    y = this.Y;
                    break;
                default:
                    break;
            }

            // if is validate position, return a new coordinate
            if (Validator.ValidatePosition(x, y))
            {
                Coordinate coor = new Coordinate(x, y);
                return coor;
            }
            else
            {
                return null;
            }
        }

        #endregion
    }
}
